#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
#      This program is free software; you can redistribute it and/or modify
#      it under the terms of the GNU General Public License as published by
#      the Free Software Foundation; either version 2 of the License, or
#      (at your option) any later version.
#
#      This program is distributed in the hope that it will be useful,
#      but WITHOUT ANY WARRANTY; without even the implied warranty of
#      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#      GNU General Public License for more details.
#
#      You should have received a copy of the GNU General Public License
#      along with this program; if not, write to the Free Software
#      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#

# Copyright (C) 2009 Iván G. Campaña N. (e-van) <ivan.campana at gmail.com>
# Released under GNU General Public License

"""
This module simplifies some common tasks needed when working with glade files and
regular application development.
"""

__all__ = ['utils',]

import sys, os, user
import gobject
import ConfigParser
import gettext
import thread
from time import sleep
import mimetypes

try:
    import pygtk
    pygtk.require("2.0")
except:
    pass
try:
    import gtk
    import gtk.glade
except:
    sys.exit(1)

MIME_ICONS = {
	'image/jpeg': "file-image",
	"text/plain": "file-text",
	"image/gif": "file-image",
	"image/x-ms-bmp": "file-image",
	"audio/midi": "file-sound",
	"audio/mpeg" : "file-sound",
	"application/java-archive" : "file-package",
	"video/mpeg": "file-video",
	"video/mp4": "file-video",
	"video/3gpp": "file-video",
	"application/x-shockwave-flash": "file-swf",
	"video/x-msvideo": "file-video"
}


GLADE_FILE = ""

def set_file(filename):
	"Establishes the current glade file to work with"

	global GLADE_FILE

	if not os.path.exists (filename):
		raise IOException

	GLADE_FILE = filename

def idle_events():
	"Checks for any pending GTK+ events to allow them to finish"

	while gtk.events_pending():
		gtk.main_iteration()

def quit(return_value = 0):
	"Exits the main loop and makes any necesary cleanup"

	gtk.main_quit()
	return sys.exit(return_value)

def run():
	"Gets the main GTK process running"
	
	gtk.gdk.threads_init()
	gobject.threads_init()

	return gtk.main()


#locale.setlocale(locale.LC_ALL, '')
#gettext.install(configuration.TEXT_DOMAIN, configuration.LOCALE_DIR, unicode=1)
#gtk.glade.bindtextdomain(configuration.TEXT_DOMAIN, configuration.LOCALE_DIR)

def do_gui_operation(function, *args, **kw):
	def idle_func():
			gtk.gdk.threads_enter()
			
			try:
					function(*args, **kw)
					#para que  se elimine de la 'lista de cosas 
					#para ejecutar' el callback ha de devolver 
					#false, por eso este return        
					return False
			finally:
					gtk.gdk.threads_leave()

	#gobject.idle_add(*callback*) anade una 'tarea pendiente' para cuando 
	#el loop de eventos este idle
	gobject.idle_add(idle_func)

def threaded_call(function, params = (), breakpoint = None, action = None):
	"Allows a function to lock the main thread of a GTK application"
	
	finish = thread.allocate_lock()
	finish.acquire()
	thread.start_new_thread(function, params)

	while finish.locked():
		if action:
			action()
		sleep(0.1)
		
		
		if breakpoint():
			finish.release()
			return False
		
		idle_events()

def FileDialog(title = "Open", parent = None, handler = None, filters = None, shortcuts = None):
	"Create a file Open/Save dialog associated with an optional parent window"

	def closeHandler(widget, response):
		widget.destroy()

	wndHandler = None
	if parent:
		wndHandler = parent.win

	fcFile = gtk.FileChooserDialog(title,
						parent = wndHandler,
						action=gtk.FILE_CHOOSER_ACTION_OPEN,
						buttons=(gtk.STOCK_CANCEL, 0, gtk.STOCK_OK, 1) )

	if shortcuts:
		for item in shortcuts:
			fcFile.add_shortcut_folder(item)

	# Filtro de archivos
	file_filter = gtk.FileFilter()

	#file_filter.set_name("Declaraciones en formato XML")
	if filters:
		for pattern in filters:
			file_filter.add_pattern(pattern.lower())
			file_filter.add_pattern(pattern.upper())

	fcFile.set_filter(file_filter)

	if handler:
		fcFile.connect("response", handler)
	else:
		fcFile.connect("response", closeHandler)

	fcFile.show()

	return

def MessageBox(message, type = 'error', window = None, buttons = 'ok'):
	"Short handler for common message boxes"

	def closeHandler(self, response):
		self.destroy()

	message_type = {
			"error" : gtk.MESSAGE_ERROR,
			"warning" : gtk.MESSAGE_WARNING,
			"info" : gtk.MESSAGE_INFO
	}

	if type not in message_type:
		type = "error"

	buttons_included = {
		"ok" : gtk.BUTTONS_OK,
		"yes_no": gtk.BUTTONS_YES_NO
	}

	if buttons not in buttons_included:
		buttons = 'ok'

	wndDialog = gtk.MessageDialog(parent = window, flags= gtk.DIALOG_MODAL, type = message_type[type], buttons = buttons_included[buttons], message_format = message)
	wndDialog.connect("response", closeHandler)
	wndDialog.show()

def query(myObject):
	print myObject.__name__
	print myObject.__doc__

	propiedades = [m for m in dir(myObject) if not callable(getattr(myObject,m))]
	print "Properties:"
	print propiedades
	metodos = [m for m in dir(myObject) if callable(getattr(myObject,m))]
	print "Methods:"
	print metodos


def load_icon(icon_name, width = 16, height = 16):
	"Loads an image from the icons folder"

	global GLADE_FILE

	icon_path = os.path.join(os.path.dirname(GLADE_FILE), "icons", icon_name)
	if os.path.exists(icon_path +".svg"):
		icon_path += ".svg"
	elif os.path.exists(icon_path +".png"):
		icon_path += ".png"
	else:
		icon_theme = gtk.icon_theme_get_default()
		try:
			icon = icon_theme.load_icon(icon_name, width, gtk.ICON_LOOKUP_USE_BUILTIN)
		except:
			icon = icon_theme.load_icon("gtk-missing-image", width, gtk.ICON_LOOKUP_USE_BUILTIN)
			
		return icon
		

	return gtk.gdk.pixbuf_new_from_file_at_size(icon_path, width, height)


def find_icon_by_name(filename):
	mimetypes.init()
	try:
		type = mimetypes.guess_type(filename)[0]
		
		icon_type = MIME_ICONS[type]
	except:
		icon_type = "gtk-missing-image"
		
	return load_icon(icon_type)
	

class BaseWindow:
	"Base class to define the widgets and methods to be used for each window"
	windowname = ''
	visible = False
	parent = None

	def __init__(self):
		#Gets the window name from the class name
		self.windowname = str(self.__class__).split(".")[1]

		if self.windowname:
			self.wTree = gtk.glade.XML(GLADE_FILE, self.windowname)
			self.win = self.wTree.get_widget(self.windowname)
			self.wTree.signal_autoconnect(self)
			self.win.connect("destroy", self.destroy)

		# Calls the 'on_load' event that we can define for each window
		self.on_load()

	def __getattr__(self, data_name):
		if data_name in self.__dict__:
			data = self.__dict__[data_name]
			return data
		else:
			widget = self.wTree.get_widget(data_name)
			if widget != None:
				self.__dict__[data_name] = widget
				return widget
			else:
				raise AttributeError, ("Attribute not found : ") + str(data_name) 

	def on_load(self):
		"Function executed after having created the base window and connected the signals"
		pass

	def show(self):
		"Calls the corresponding show method for the widget associated with the window"

		if self.win:
			self.win.show()
			self.visible = True

	def hide(self):
		"Hides the current window"

		if self.win:
			self.win.hide()
			self.visible = False

	def isVisible(self):
		return self.visible

	def set_modal(self, bool = False):
		self.win.set_modal(bool)
		
	def set_title(self, title):
		self.win.set_title(title)

	def destroy(self, *args):
		self.win.destroy()

	def set_parent(self, parent_window = None):
		"Establishes the parent class and the parent window for the current object"
		if parent_window:
			self.parent = parent_window
			self.win.set_transient_for(parent_window.win)

def make_list(widget, columns = {}, type = "list"):
	"Allows the creation of a list suitable with a TreeView control"

	if len(columns) > 0:
		model_form = []
		gui_columns = {}
		column_number = 0
		first_text = -1

		# Get the classname of the object
		widget_class = widget.class_path().split(".")[-1]
		#print widget.get_name()

		try:
			widget.clear()
		except:
			pass

		for column in columns:
			if not column.has_key('type'):
				column['type'] = 'str'

			if column['type'] == "icon":
				model_form.append('gtk.gdk.Pixbuf')
			elif column['type'] != "pixbuf":
				model_form.append(column['type'])

			# add column to treeview
			if widget_class != "GtkComboBox":
				# create the TreeViewColumn to display the data
				gui_columns[column['title']] = gtk.TreeViewColumn(column['title'])

				# add a new column
				widget.append_column(gui_columns[column['title']])
			else:
				gui_columns[column['title']] = widget

			# create a CellRendererText to render the data
			if column['type'] == "str":
				cell = gtk.CellRendererText()
				if first_text == -1:
					first_text = column_number
			elif column['type'] in ("pixbuf", "icon"):
				cell = gtk.CellRendererPixbuf()
				column['expand'] = "false"

			if column.has_key("properties"):
				if len(column["properties"])>0:
					for property_name in column["properties"]:
						cell.set_property(property_name, column["properties"][property_name])

			expand = True
			if column.has_key("expand"):
				if column['expand'].upper() ==  "FALSE":
					expand = False

			visible = True
			if column.has_key('visible'):
				if column['visible'].upper() == "FALSE":
					visible = False

			if visible:
				# add the cell to the column and allow it to expand
				if column.has_key("pack_end"):
					gui_columns[column['title']].pack_end(cell, expand)
				else:
					gui_columns[column['title']].pack_start(cell, expand)


			# set the cell "text" attribute to column 0 - retrieve text
			# from that column in treestore
			if column['type'] == "str":
				if visible:
					gui_columns[column['title']].add_attribute(cell, 'text', column_number)

				if widget_class != "GtkComboBox":
					gui_columns[column['title']].set_sort_column_id(column_number)

					if column.has_key("searchable"):
							widget.set_search_column(column_number)
				column_number += 1
				
			elif column['type'] == "icon":
				gui_columns[column['title']].add_attribute(cell, 'pixbuf', column_number)
				column_number += 1


		if widget_class in ("GtkComboBox") or type == "list":
			list_creator = "gtk.ListStore(" + ",".join(model_form) + ")"
		elif widget_class in ("GtkTreeView") or type == "tree":
			list_creator = "gtk.TreeStore(" + ",".join(model_form) + ")"

		list_model = eval(list_creator)
		widget.set_model(list_model)


class AppSettings:
	filename = ""
	dirname = ""
	config = None

	def __init__(self, app_name = "ezGlade", filename = "config.cfg", mode = "D"):

		if mode == "F":
			self.filename = os.path.join(user.home, filename)
		else:
			self.dirname = os.path.join(user.home, "." + app_name)
			self.filename = os.path.join(self.dirname, filename)

			if not os.path.isdir(self.dirname):
				os.makedirs(self.dirname, mode=0700)

		self.config = ConfigParser.ConfigParser()

	def is_valid(self):
		if not os.path.exists(self.filename):
			return False

		return True

	def get_value(self, section, option, default = None):
		value = default

		try:
			value = self.config.get(section, option)
		except ConfigParser.NoSectionError:
			pass
		except ConfigParser.NoOptionError:
			pass

		return value

	def set_value(self, section, item, value):
		if not self.config.has_section(section):
			self.config.add_section(section)

		self.config.set(section, item, value)

	def remove_value(self, section, option):
		try:
			self.config.remove_option(section, option)
		except:
			return False

		return True

	def get_sections(self, skip_sections = None):
		if skip_sections:
			return [ f for f in self.config.sections() if f not in skip_sections]

		return self.config.sections()

	def read(self):
		self.config.read(self.filename)

	def save(self):
		f = file(self.filename, 'w')
		self.config.write(f)
		f.close()

################################################################################
#
#          This code needs revision it is more likely to be discarded
#
################################################################################
# Progress Dialog
# --------------------------------------------
# Code taken from Serpentine
#

def hig_label (text = None):
    lbl = gtk.Label ()
    lbl.set_alignment (0, 0)
    if text:
        lbl.set_markup (text)
    lbl.set_selectable (False)
    lbl.set_line_wrap (True)
    return lbl

def hig_dialog (dialog):
	"""
	Creates a gtk.Dialog and adds a gtk.Alignment has it's child.
	Returns the gtk.Alignment instance.

	See: http://bugzilla.gnome.org/show_bug.cgi?id=163850
	"""
	vbox = gtk.VBox (False, 24)
	vbox.set_border_width (12)
	vbox.show ()
	dialog.vbox = vbox

	hbox = gtk.HButtonBox ()
	hbox.set_spacing (6)
	hbox.set_layout (gtk.BUTONBOX_END)
	hbox.show ()
	vbox.pack_end (hbox)
	return dialog, vbox

class HigProgress (gtk.Window):
	"""
		HigProgress returns a window that contains a number of properties to
		access what a common Progress window should have.
	"""
	def __init__ (self):
		gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
		self.set_border_width (6)
		self.set_resizable (False)
		self.set_title ('')
		# defaults to center location
		self.set_position (gtk.WIN_POS_CENTER)
		self.set_modal(True)
		self.connect ("delete-event", self.__on_close)

		# main container
		main = gtk.VBox (spacing = 12)
		main.set_spacing (12)
		main.set_border_width (6)
		main.show()
		self.add (main)

		# primary text
		alg = gtk.Alignment ()
		alg.set_padding (0, 6, 0, 0)
		alg.show()
		main.pack_start (alg, False, False)
		lbl = hig_label()
		lbl.set_selectable (False)
		lbl.show()
		self.__primary_label = lbl
		alg.add (lbl)

		# secondary text
		lbl = hig_label()
		lbl.set_selectable (False)
		lbl.show()
		main.pack_start (lbl, False, False)
		self.__secondary_label = lbl

		# Progress bar
		vbox = gtk.VBox()
		vbox.show()
		main.pack_start (vbox, False, False)

		prog = gtk.ProgressBar ()
		prog.show()
		self.__progress_bar = prog
		vbox.pack_start (prog, expand = False)

		lbl = hig_label ()
		lbl.set_selectable (False)
		lbl.show ()
		self.__sub_progress_label = lbl
		vbox.pack_start (lbl, False, False)

		lbl = hig_label ()
		lbl.set_selectable (False)
		lbl.show ()
		self.__sub_progress_label = lbl
		vbox.pack_start (lbl, False, False)

		# Buttons box
		bbox = gtk.HButtonBox ()
		bbox.set_layout (gtk.BUTTONBOX_END)
		bbox.show ()

		# Cancel Button
		cancel = gtk.Button (gtk.STOCK_CANCEL)
		cancel.set_use_stock (True)
		cancel.show ()
		self.__cancel = cancel
		bbox.add (cancel)
		main.add (bbox)

		# Close button, which is hidden by default
		close = gtk.Button (gtk.STOCK_CLOSE)
		close.set_use_stock (True)
		close.hide ()
		bbox.add (close)
		self.__close = close

	primary_label = property (lambda self: self.__primary_label)
	secondary_label = property (lambda self: self.__secondary_label)
	progress_bar = property (lambda self: self.__progress_bar)
	sub_progress_label = property (lambda self: self.__sub_progress_label)
	cancel_button = property (lambda self: self.__cancel)
	close_button = property (lambda self: self.__close)

	def primary_text (self, text):
		self.primary_label.set_markup ('<span weight="bold" size="larger">'+text+'</span>')
		self.set_title (text)

	primary_text = property (fset = primary_text)

	def secondary_text (self, text):
		self.secondary_label.set_markup (text)

	secondary_text = property (fset = secondary_text)

	def progress_fraction (self, fraction):
		self.progress_bar.set_fraction (fraction)

	progress_fraction = property (fset = progress_fraction)

	def progress_text (self, text):
		self.progress_bar.set_text (text)
	progress_text = property (fset = progress_text)

	def sub_progress_text (self, text):
		self.sub_progress_label.set_markup ('<i>'+text+'</i>')
	sub_progress_text = property (fset = sub_progress_text)

	def __on_close (self, *args):
		if not self.cancel_button.get_property ("sensitive"):
			return True
		# click on the cancel button
		self.cancel_button.clicked ()
		# let the clicked event close the window if it likes too
		return True

gobject.type_register (HigProgress)
